import {Injectable} from '@angular/core';
import {LoginUser} from '../entities/user/login-user';
import {storage, StringTool} from '../tools';
import {UserMenu} from '../entities/user/user-menu';
import {Observable, of} from 'rxjs';
import {catchError, concatMap, map, tap} from 'rxjs/operators';
import {environment} from '../../environments/environment';
import {HttpClient, HttpHeaders} from '@angular/common/http';
import {ResponseEntity} from './http/response-entity';

@Injectable({
  providedIn: 'root'
})
export class LoginUserService {

  private server = environment.serverName.system;
  private serverPrefix = environment.service.servicePrefix;

  private LOGIN_USER_KEY = 'barn_login_user';
  private LOGIN_USER_REFRESH_TOKEN_KEY = 'login_user_refresh_token';
  private user: LoginUser;
  private _MENUS: UserMenu[];

  constructor(private http: HttpClient) {
  }

  get currentUser(): LoginUser {
    if (this.user) {
      return this.user;
    }
    const userStr = storage.SStorage.getItem(this.LOGIN_USER_KEY);
    if (userStr) {
      let user = null;
      try {
        user = JSON.parse(userStr);
      } catch (e) {
        console.error(e);
      }
      return user;
    }
    return null;
  }

  set currentUser(user: LoginUser) {
    if (!user) {
      this.user = user;
      console.warn('current value can not be null!');
      return;
    }
    try {
      storage.SStorage.setItem(this.LOGIN_USER_KEY, JSON.stringify(user));
      this.user = user;
    } catch (e) {
      console.log(e);
    }
  }

  get refreshToken(): string {
    return storage.LStorage.getItem(this.LOGIN_USER_REFRESH_TOKEN_KEY);
  }

  set refreshToken(token: string) {
    storage.LStorage.setItem(this.LOGIN_USER_REFRESH_TOKEN_KEY, token);
  }

  clean(): void {
    this._MENUS = [];
    this.user = null;
    storage.LStorage.removeItem(this.LOGIN_USER_REFRESH_TOKEN_KEY);
    storage.SStorage.removeItem(this.LOGIN_USER_KEY);
  }

  logout(): Observable<any> {
    const out = this.http.get(this.serverPrefix + '/logout');
    out.subscribe();
    return out;
  }

  get MENUS(): Observable<UserMenu[]> {
    if (this._MENUS && this._MENUS.length) {
      return of(JSON.parse(JSON.stringify(this._MENUS)) as UserMenu[]);
    } else {
      const options = {
        headers: new HttpHeaders({
          Authorization: this.currentUser?.token
        })
      };

      const url = this.serverPrefix + '/' + this.server + '/menu-user/getUserMenus';

      return this.http.get<ResponseEntity<UserMenu[]>>(url, options)
        .pipe(
          map(value => {
            if (value.status === '0') {
              return value.data;
            } else {
              throw value;
            }
          }),
          tap(value => {
            if (value && value.length) {
              this._MENUS = JSON.parse(JSON.stringify(value));
            }
            return value;
          }),
          catchError((error) => {

            if (error.status === 401) {
              // 刷新token
              return this.retry(() => {
                // 刷新之后继续之前的调用
                const options2 = {
                  headers: new HttpHeaders({
                    Authorization: this.currentUser?.token
                  })
                };
                return this.http.get<ResponseEntity<UserMenu[]>>(url, options2).pipe(
                  map(value => {
                    if (value.status === '0') {
                      return value.data;
                    } else {
                      throw value;
                    }
                  }),
                );
              });
            } else {
              throw error;
            }
          })
        );
    }
  }


  private retry(retry: () => Observable<UserMenu[]>): Observable<UserMenu[]> {
    const refreshUrl = StringTool.concatUrl(this.serverPrefix, 'login/ps/refresh');
    // 刷新token
    return this.http.post<ResponseEntity<LoginUser>>(refreshUrl, {refreshToken: this.refreshToken})
      .pipe(
        map(value => value.data),
        concatMap(value => {
          this.currentUser = value;
          this.refreshToken = value.refreshToken;
          // 刷新之后继续之前的调用
          return retry();
        }),
        catchError((error) => {
          console.log(error);
          if (error.status === 500 && error.error.status === 'TOKEN_EXPIRED') {
            throw {status: 'TOKEN_EXPIRED'};
          } else {
            throw error;
          }
        })
      );
  }

}
